﻿using NicoView.Model.Service.Comments.BodyTexts;
using NicoView.Model.Share.Service;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace NicoView.Model.Service.Comments
{
    internal class NicoCommentBodyParser
    {

        internal enum InternalParserResultType
        {
            None,
            OwnerShowCommand,
            NoneShowCommand,
            Command,
            AnchorHtmlTag,
            HtmlTag,
            Text
        }

        internal class InternalParserResult
        {
            internal int Index { get; private set; }

            internal string Text { get; private set; }


            internal bool Succeed
            {
                get
                {
                    return !string.IsNullOrEmpty(Text);
                }
            }

            internal INicoCommentBodyText CreateNicoCommentBodyText()
            {
                return CreateNicoCommentBodyTextFunc(Text);
            }


            internal InternalParserResultType Type { get; private set; }


            internal Func<string,INicoCommentBodyText> CreateNicoCommentBodyTextFunc;

            internal static INicoCommentBodyText CreateNicoHtmlTagBodyText(string text)
            {
                var parser = new NicoCommentBodyParser();
                var htmlTag = new NicoHtmlTagBodyText(parser.HtmlParse(text));
                return htmlTag;
            }

            internal static INicoCommentBodyText CreateUrlNicoCommentBodyText(string text)
            {
                return new NicoUrlBodyText(text);
            }

            internal static INicoCommentBodyText CreateNicoVideoIdBodyText(string text)
            {
                return new NicoVideoIdBodyText(text);
            }

            internal static INicoCommentBodyText CreateNicoLiveIdBodyText(string text)
            {
                return new NicoLiveIdBodyText(text);
            
            }

            internal static INicoCommentBodyText CreateNicoCommentCommandBodyText(string text)
            {
                return new NicoCommentCommandBodyText(text);
            }

            internal static INicoCommentBodyText CreatePlaneText(string text)
            {
                return new NicoPlainBodyText(text);
            }

            internal static INicoCommentBodyText CreateNicoLineBreakBodyText(string text)
            {
                return new NicoLineBreakBodyText(text);
            }

            internal static INicoCommentBodyText CreateNicoComunityIdBodyText(string text)
            {
                return new NicoComunityIdBodyText(text);
            }


            internal static InternalParserResult CreateResult(int range, string text, Func<string, INicoCommentBodyText> createNicoCommentBodyText,InternalParserResultType type)
            {
                var result = new InternalParserResult();
                result.Index = range;
                result.Text = text;
                result.CreateNicoCommentBodyTextFunc = createNicoCommentBodyText;
                result.Type = type;
                return result;
            }
        }

        internal class InternalParser
        {
            internal string Body { get; set; }


            internal InternalParser()
            {


                
            }

            internal bool ParseRequest
            {
                get
                {
                    return Body.Length > 0;
                }
            }






            internal InternalParserResult ParseUrl(Match match)
            {
                return parseRegex( URL,match, (g) =>
                {
                    return InternalParserResult.CreateUrlNicoCommentBodyText;
                }, InternalParserResultType.Text);
            }

            internal InternalParserResult ParseVideoId(Match match)
            {
                return parseRegex(VIDEO_ID, match, (g) =>
                {
                    return InternalParserResult.CreateNicoVideoIdBodyText;
                }, InternalParserResultType.Text);
            }

            internal InternalParserResult ParseLiveId(Match match)
            {
                return parseRegex(LIVE_ID, match, (m) =>
                {
                    return InternalParserResult.CreateNicoLiveIdBodyText;

                }, InternalParserResultType.Text);
            }

            internal InternalParserResult ParseComunityId(Match match)
            {
                return parseRegex(COMMUNITY_ID, match, (g) =>
                {
                    return InternalParserResult.CreateNicoComunityIdBodyText;
                }, InternalParserResultType.Text);
            }

            internal InternalParserResult ParseNoneShowCommand(Match match)
            {
                return parseRegex(NONE_SHOW_COMMAND, match, (g) =>
                {
                    return (s) =>
                    {
                        return new NicoNoneShowCommandBodyText(s);
                    };
                }, InternalParserResultType.NoneShowCommand);
            }


            internal InternalParserResult ParseHtmlTag(Match match)
            {

                return parseRegex(HTML_TAG, match, (g) =>
                {
                    return InternalParserResult.CreateNicoHtmlTagBodyText;
                }, InternalParserResultType.HtmlTag);
            }

            internal InternalParserResult ParseAnchorHtmlTag(Match match)
            {
                return parseRegex(ANCHORHTML_TAG, match, (g) =>
                {
                    return (s) =>
                    {
                        var parser = new NicoCommentBodyParser();
                        var tagMatch = anchorHtmlTagRegex.Match(g.Value);

                        return new NicoAnchorHtmlTagBodyText(htmlTagRegex.Replace(tagMatch.Groups[2].Value, ""), tagMatch.Groups[1].Value, parser.AnchorHtmlParse(s));
                    };
                }, InternalParserResultType.AnchorHtmlTag);
            }

            internal InternalParserResult ParseNameRegist(Match match)
            {
                return parseRegex(NAME_REGEST, match, (g) =>
                {
                    return (s) =>
                    {
                        var nameMatch = nameRegistRegex.Match(g.Value);
                        return new NicoNameRegistRequestBodyText(g.Value, nameMatch.Groups[1].Value);
                    };
                },InternalParserResultType.Text);
            }

            internal InternalParserResult ParseLiveOwnerShowCommand(NotifyNicoComment comment,Match match)
            {
                if (comment==null || ( (comment.UserType != CommentUserType.Owner) &&( comment.CommentType == CommentType.Live)) )
                {
                    return new InternalParserResult();
                }

                return parseRegex(OWNER_SHOW_COMMAND, match, (g) =>
                {
                    return (s) =>
                    {
                        var body = match.Value;
                        var parser = new NicoCommentBodyParser();

                        return new NicoOwnerShowCommand(body);
                    };
                }, InternalParserResultType.OwnerShowCommand);
            }

            internal InternalParserResult ParseNoAnchor(Match match)
            {
                return parseRegex(NOANCHOR, match, (g) =>
                {
                    var noMatch = noAnchorRegex.Match(g.Value);
                    if (noMatch.Groups[3].Length > 0)
                    {

                        return(s)=>{
                            return new NicoRangeNoAnchorBodyText(int.Parse(noMatch.Groups[1].Value), int.Parse(noMatch.Groups[3].Value));
                        }; 
                    }
                    else
                    {
                        return (s) =>
                        {
                            return new NicoSingleNoAnchorBodyText(int.Parse(noMatch.Groups[1].Value));
                        };
                    }
                }, InternalParserResultType.Text);
            }

            internal InternalParserResult ParseLineBreak(Match match)
            {
                return parseRegex(LINE_BREAK, match, (m) =>
                {
                    return InternalParserResult.CreateNicoLineBreakBodyText;
                }, InternalParserResultType.Text);
            }

            internal InternalParserResult ParseCommentCommand(Match match)
            {
                return parseRegex(COMMANDBODY, match, (m) =>
                {
                    return InternalParserResult.CreateNicoCommentCommandBodyText;
                }, InternalParserResultType.Text);
            }

            private InternalParserResult parseRegex(string key,Match match, Func<Group, Func<string,INicoCommentBodyText>> handler,InternalParserResultType type)
            {
                if (match.Success)
                {

                    var group = match.Groups[key];

                    if (group.Success)
                    {

                        return InternalParserResult.CreateResult(group.Index, group.Value, handler(group), type);

                    }
                }
                

                return new InternalParserResult();
            }


            private IEnumerable<InternalParserResult> _privateParse(ICollection<INicoCommentBodyText> texts, Func<InternalParserResult[]> func)
            {
                InternalParserResult[] internalResults = func();
                return from result in internalResults
                                   orderby result.Index
                                   where result.Succeed
                                   select result;

            }





            internal void Parse(NotifyNicoComment comment, ICollection<INicoCommentBodyText> texts, Func<InternalParserResult[], IEnumerable<InternalParserResult>> func)
            {
                int startIndex = 0;
                var matches = _regex.Matches(Body);
                
                foreach (Match match in matches)
                {
                    InternalParserResult[] parseResultsAll = {
                        ParseLiveOwnerShowCommand(comment,match),
                        ParseNoneShowCommand(match),
                        ParseAnchorHtmlTag(match),
                        ParseUrl(match),
                        ParseLineBreak(match),
                        ParseVideoId(match),
                        ParseLiveId(match),
                        ParseComunityId(match),
                        ParseNoAnchor(match),
                        ParseNameRegist(match),
                        ParseCommentCommand(match),
                        ParseHtmlTag(match)
                    };

                    IEnumerable<InternalParserResult> parseResults = func(parseResultsAll);
                    var planeText = Body;
                    
                    INicoCommentBodyText resultText = null;
                    if (parseResults.Count() > 0)
                    {

                        var parseResult = parseResults.First();

                        resultText = parseResult.CreateNicoCommentBodyText();


                        planeText = Body.Substring(startIndex, parseResult.Index - startIndex);

                        startIndex += parseResult.Text.Length;

                    }

                    if (planeText.Length > 0)
                    {
                        startIndex += planeText.Length;

                        texts.Add(InternalParserResult.CreatePlaneText(planeText));
                    }

                    if (resultText != null)
                    {
                        texts.Add(resultText);
                    }



                    
                }
                if (Body.Length > startIndex)
                {
                    texts.Add(new NicoPlainBodyText(Body.Substring(startIndex,Body.Length - startIndex)));
                }
                
                
            }

            internal void HtmlParse(ICollection<INicoCommentBodyText> texts)
            {
                Parse(null,texts, (results) =>
                {
                    return from result in results
                           orderby result.Index
                           where result.Succeed && result.Type != InternalParserResultType.AnchorHtmlTag && result.Type != InternalParserResultType.HtmlTag
                           select result;
                });
            }

            internal void AnchorHtmlParse(ICollection<INicoCommentBodyText> texts)
            {
                Parse(null,texts, (results) =>
                {
                    return from result in results
                           orderby result.Index
                           where result.Succeed && result.Type != InternalParserResultType.AnchorHtmlTag
                           select result;
                });
            }


            internal void Parse(NotifyNicoComment comment, ICollection<INicoCommentBodyText> texts)
            {


                Parse(comment,texts, (results) =>
                {
                    return from result in results
                           orderby result.Index
                           where result.Succeed 
                           select result;
                });


                

            }

            


            private static Regex createRegex()
            {
                var dic = new Dictionary<string, string>();
                dic.Add(ANCHORHTML_TAG, NicoFormat.ANCHOR_HTML_TAG);
                dic.Add(URL, NicoFormat.URL_FORMAT);
                dic.Add(VIDEO_ID, NicoFormat.VIDEOID_FORMAT);
                dic.Add(LIVE_ID, NicoFormat.LIVEID_FORMAT);
                dic.Add(COMMUNITY_ID, NicoFormat.COMMUNITY_FORMAT);
                dic.Add(LINE_BREAK, NicoFormat.LINE_BREAK);
                dic.Add(NOANCHOR, NicoFormat.NOANCHOR_FORMAT);
                dic.Add(HTML_TAG, NicoFormat.HTML_TAG);
                dic.Add(NAME_REGEST, NicoFormat.NAME_REGIST_FORMAT);
                dic.Add(OWNER_SHOW_COMMAND, NicoFormat.OWNER_SHOW_COMMAND_FORMAT);
                //dic.Add(BSP_COMMAND, NicoFormat.BSP_COMMAND_FORMAT);
                dic.Add(NONE_SHOW_COMMAND, NicoFormat.NONE_SHOW_COMMAND_FORMAT);
                dic.Add(COMMANDBODY, NicoFormat.COMMENT_COMMAND_FORMAT);
                

                string regexStr = "";

                foreach (var pair in dic)
                {
                    regexStr += "(?<" + pair.Key + ">" + pair.Value + ")|";
                }
                regexStr = regexStr.Substring(0, regexStr.Length - 1);

                return new Regex(regexStr);
            }

            private static readonly Regex _regex = createRegex();

            private const string ANCHORHTML_TAG = "anchorHtmlTag";

            private const string URL = "url";

            private const string VIDEO_ID = "videoId";

            private const string LIVE_ID = "liveId";

            private const string COMMUNITY_ID = "communityId";

            private const string LINE_BREAK = "lineBreak";

            private const string NOANCHOR = "noAnchor";

            private const string HTML_TAG = "htmlTag";

            private const string NAME_REGEST = "nameRegest";

            private const string OWNER_SHOW_COMMAND = "ownerShowCommand";

            private const string BSP_COMMAND = "bspCommand";

            private const string COMMANDBODY = "commandBody";

            private const string NONE_SHOW_COMMAND = "noneShowCommand";

            private static readonly Regex nameRegistRegex = new Regex(NicoFormat.NAME_REGIST_FORMAT);

            private static readonly Regex anchorHtmlTagRegex = new Regex(NicoFormat.ANCHOR_HTML_TAG);

            private static readonly Regex htmlTagRegex = new Regex(NicoFormat.HTML_TAG);

            private static readonly Regex noAnchorRegex = new Regex(NicoFormat.NOANCHOR_FORMAT);

            /*
            private static readonly Regex _anchorHtmlRegex = new Regex(NicoFormat.ANCHOR_HTML_TAG);

            private static readonly Regex _urlRegex = new Regex(NicoFormat.URL_FORMAT);

            private static readonly Regex _videoRegex = new Regex(NicoFormat.VIDEOID_FORMAT);

            private static readonly Regex _liveIdRegex = new Regex(NicoFormat.LIVEID_FORMAT);

            private static readonly Regex _comunityRegex = new Regex(NicoFormat.COMMUNITY_FORMAT);

            private static readonly Regex _lineBreakRegex = new Regex(NicoFormat.LINE_BREAK);

            private static readonly Regex _noAnchorRegex = new Regex(NicoFormat.NOANCHOR_FORMAT);

            private static readonly Regex _htmlTagRegex = new Regex(NicoFormat.HTML_TAG);

            private static readonly Regex _nameRegistRegex = new Regex(NicoFormat.NAME_REGIST_FORMAT);

            private static readonly Regex _commandBodyRegex = new Regex(NicoFormat.COMMENT_COMMAND_FORMAT);

            private static readonly Regex _ownerShowCommand = new Regex(@"\A(/perm)(\s+)(.*)");

            private static readonly Regex _userShowCommand = new Regex(@"\A(/press)(\s+)(show)(\s+)([@＠^]+)(\s+)");
            */
            
        }

        internal NicoCommentBodyParser()
        {


            this.bodyTextList = new List<INicoCommentBodyText>();

            this.parser = new InternalParser();

        }

        

        private List<INicoCommentBodyText> bodyTextList;

        private InternalParser parser;







        private void initializeParse(string body)
        {
            bodyTextList.Clear();
            this.parser.Body = body;
        }



        internal INicoCommentBodyText[] HtmlParse(string body)
        {
            initializeParse(body);

            if (parser.ParseRequest)
            {
                this.parser.HtmlParse(this.bodyTextList);
            }

            return bodyTextList.ToArray();
            
        }


        internal INicoCommentBodyText[] Parse(NotifyNicoComment comment, string body)
        {
            initializeParse(body);

            

            if (parser.ParseRequest)
            {
                this.parser.Parse(comment,this.bodyTextList);
            }

            return bodyTextList.ToArray();
        }


        internal INicoCommentBodyText[] ParseCommandText(string body)
        {
            initializeParse(body);

            if (parser.ParseRequest)
            {
                this.parser.Parse(null,this.bodyTextList);
            }

            return this.bodyTextList.ToArray();
        }

        internal ICollection<INicoCommentBodyText> AnchorHtmlParse(string body)
        {
            initializeParse(body);

            

            if (parser.ParseRequest)
            {
                this.parser.AnchorHtmlParse(this.bodyTextList);
            }

            return bodyTextList.ToArray();
        }
    }
}
